# -*- coding: utf-8 -*-

# @File   : common
# @Author : Yupeng
# @Date   : 2018/11/17

from django.utils.translation import ugettext as _
from enum import Enum, IntEnum
import os
from apps.storage.models.file import DataContentFile, FileContentType
import apps.storage.models.data as data_models
from typing import Callable
from hte.error.models import HTEError,HTEException



class DataMode(IntEnum):
    """
    Excel操作模式
    """
    READ = 1  # 导入数据
    WRITE = 2  # 导出数据
    TEMPLATE = 3  # 生成导入模板

    @property
    def description(self):
        if self == 1:
            return "read"
        elif self == 2:
            return "write"
        elif self == 3:
            return "template"

class ParsingErrorEnum(Enum):
    FIELD_MISSING = _("Field %(o1)s is missing. ")
    UNKNOWN_FIELD = _("Unknown field \"%(o1)s\".")
    VALUE_MISSING = _('Value for field "%(o1)s" is required but is missing.')
    INVALID_VALUE = _('Value "%(o1)s" is invalid for type "%(o2)s". (Field "%(o3)s")')
    NO_ELEMENTS_IN_ARRAY = _('No elements found in array "%(o1)s".')
    NO_META = _('Data "%(o1)s" has no metadata.')
    NO_DATA = _('Data "%(o1)s" has metadata but no data content.')
    TEMPLATE_NOT_FOUND = _('Template "%(o1)s" not found.')
    FILE_NOT_FOUND = _('File "%(o1)s" does not exist the archive file.')
    BAD_TEMPLATE = _(
        "Template file is corrupted. Please re-generate a template. "
    )
    INVALID_CHOICE_VALUE = _('Value "%(o1)s" is not a valid choice. Allowed choices are %(o2)s.')
    XMLSyntaxError = _("Bad XML Syntax: %(o1)s.")
    JSONSyntaxError = _("Bad JSON Syntax: %(o1)s.")
    NODE_MISSING = _("Node \"%(o1)s\" is missing from node \"%(o2)s\".")
    XML_ATTRIBUTE_MISSING = _("Attribute \"%(o1)s\" is missing from node \"%(o2)s\".")
    TEMPLATE_DOES_NOT_EXIST = _("Template (id=%(o1)s) does not exist.")
    EXCESSIVE_FILES_OR_IMAGES = _("Field \"%(o1)s\" does not allow multiple files (images).")


class ParsingError(Exception):

    def __init__(self, error, no_position_info=False, **kwargs):
        self.is_api = True
        self.message = error.value % kwargs
        self.error = error
        self.no_position_info = no_position_info

    def __str__(self):
        return self.__unicode__()

    def __repr__(self):
        return self.__unicode__()

    def __unicode__(self):
        return self.message

    def add_detail(self, message):
        self.message += message


class FieldParser:
    """
    抽象基类
    """

    def __init__(self, request_username, _file_dir=None):
        self._file_dir = _file_dir
        self._request_username = request_username
        self._file_path_system_path_map = {}

    # def parse_single_file_or_image(self, field_meta, file_path):

    def parse_field(self, field_meta, raw_value):
        t = field_meta.field_type

        if t.is_string:
            return self.parse_string(field_meta, raw_value)
        elif t.is_number:
            return self.parse_number(field_meta, raw_value)
        elif t.is_range:
            return self.parse_range(field_meta, raw_value)
        elif t.is_image or t.is_file:
            return self.parse_file_image(field_meta, raw_value)
        elif t.is_choice:
            return self.parse_choice(field_meta, raw_value)

    def parse_string(self, field_meta, raw_value):
        raise NotImplementedError

    def parse_number(self, field_meta, raw_value):
        raise NotImplementedError

    def parse_range(self, field_meta, raw_value):
        raise NotImplementedError

    def parse_file_image(self, field_meta, raw_value):
        raise NotImplementedError

    def parse_choice(self, field_meta, raw_value):
        choices = []
        choices.extend(field_meta['misc'].get('opt', []))
        for group in field_meta['misc'].get('grp', []):
            choices.extend(group.get('items', []))
        if str(raw_value) not in choices:
            raise ParsingError(ParsingErrorEnum.INVALID_CHOICE_VALUE, o1=raw_value,
                               o2=', '.join(choices))
        return str(raw_value)


class FieldSerializer:
    """
    抽象基类
    """

    def serialize_field(self, mongo_value, field_meta):
        t = field_meta.field_type

        if t.is_string:
            return self.serialize_string(mongo_value, field_meta)
        elif t.is_number:
            return self.serialize_number(mongo_value, field_meta)
        elif t.is_range:
            return self.serialize_range(mongo_value, field_meta)
        elif t.is_image:
            return self.serialize_image(mongo_value, field_meta)
        elif t.is_file:
            return self.serialize_file(mongo_value, field_meta)
        elif t.is_choice:
            return self.serialize_choice(mongo_value, field_meta)

    def serialize_string(self, mongo_value, field_meta):
        return str(mongo_value)

    def serialize_number(self, mongo_value, field_meta):
        raise NotImplementedError

    def serialize_range(self, mongo_value, field_meta):
        raise NotImplementedError

    def serialize_image(self, mongo_value, field_meta):
        raise NotImplementedError

    def serialize_file(self, mongo_value, field_meta):
        raise NotImplementedError

    def serialize_choice(self, mongo_value, field_meta):
        return str(mongo_value)


class DataHandler:
    def __init__(self, mode: DataMode, template):
        self._mode = mode
        self._template = template
        self.meta_id_list = []

    def read_data(self, fp_or_path, uploaded_by, file_dir=None, progress_handler: Callable[[float], None] = None):
        raise NotImplementedError

    def write_data(self, data_meta):
        raise NotImplementedError

    def save(self, fp_or_path):
        raise NotImplementedError

    def generate_template(self, number_of_data=3, elements_per_array=1, rows_per_table=1):
        raise NotImplementedError

    def roll_back(self):
        data_models.DataMeta.importing_objects.filter(id__in=self.meta_id_list).delete()

    def filter_fields(self, excluded_fields=(), included_fields=()):
        if self._mode != DataMode.WRITE:
            raise ValueError("Cannot filter fields in write mode.")
        self._template.filter_fields(excluded_fields, included_fields)
